package ths

import (
	"fmt"
	"gitee.com/gcom/gbox/errors"
	"gitee.com/gcom/stockdock/comm"
	s "gitee.com/gcom/stockdock/core/support"
	"gitee.com/gcom/stockdock/crawler"
	"gitee.com/gcom/stockdock/spider/csrc"
	"gitee.com/gcom/stockdock/spider/ths"
	"gitee.com/gcom/stockdock/spider/xueqiu"
	"time"
)

// 更新股票每日报价
func UpdateStockQuote(ctx s.TracerCtx) error {
	list, err := csrc.GetStockAll(ctx.DB)
	if err != nil {
		s.WriteErrEvent(ctx, "查询股票列表失败", err)
		return err
	}
	s.WriteEvent(ctx, fmt.Sprintf("共计股票%d支", len(list)))

	// 交易日判断
	var date = comm.DefaultDateTime
	if xq, err := xueqiu.GetStockQuote0(ctx, []string{"SH000001"}); err != nil {
		s.WriteErrEvent(ctx, "查询当前交易日失败", err)
		return err
	} else {
		date = time.Time(xq[0].Timestamp).Truncate(24 * time.Hour)
	}
	if time.Now().Truncate(24*time.Hour) != date.Truncate(24*time.Hour) {
		if ctx.Req == nil { // from Job schedule
			s.WriteEvent(ctx, "今日非交易日，忽略同花顺股票日报价数据抓取定时任务")
			return nil
		} else {
			s.WriteEvent(ctx, fmt.Sprintf("抓取交易日%s数据", date.Format("2006-01-02")))
		}
	}

	var count, nodata, total = 0, 0, len(list)
	var codeCh = make(chan string)
	var sem = make(chan int)
	defer close(codeCh)
	var entities = make([]*ths.StockQuoteThsEntity, 0, 200)
	onValue := func(req crawler.CrawlRequest, ind *ths.StockQuoteThs) {
		if ind.Volume == 0 {
			nodata += 1
			s.WriteEvent(ctx, fmt.Sprintf("[%d,%d/%d]获取股票(%s:%s)指标，股票停牌", count, nodata, total, ind.Code, ind.Name))
		} else {
			count += 1
			s.WriteEvent(ctx, fmt.Sprintf("[%d,%d/%d]获取股票(%s:%s)指标正常", count, nodata, total, ind.Code, ind.Name))
			ind.Date = date
			entities = append(entities, &ths.StockQuoteThsEntity{StockQuoteThs: ind})
		}

		if len(entities) == 200 || (count+nodata == total && len(entities) > 0) {
			_, err := ths.UpdateStockIndex(ctx.DB, entities)
			if err != nil {
				s.WriteEvent(ctx, fmt.Sprintf("写入数据库失败，%v", err))
				close(sem)
				return
			}
			s.WriteEvent(ctx, fmt.Sprintf("[%d,%d/%d]写入数据库%d条", count, nodata, total, len(entities)))
			entities = entities[:0]
		}
		sem <- 1 // produce one ticket
	}
	onError := func(req crawler.CrawlRequest, err error) {
		ctx.Logger.Errorf("获取股票指标失败, %e", err)
		close(sem)
	}
	onDiscard := func(req crawler.CrawlRequest, err error) {
		nodata += 1
		s.WriteErrEvent(ctx, fmt.Sprintf("[%d,%d/%d](%s)%s", count+1, nodata, total,
			req.(*ths.StockQuoteCrawlRequest).Code, errors.Cause(err).Error()), err)
		sem <- 1 // produce one ticket
	}

	go func() {
		ths.GetStockQuote(ctx, codeCh, onValue, onError, onDiscard)
	}()

	for i, v := range list {
		codeCh <- v.Code

		select {
		case _, ok := <-sem: // consume 1 ticket
			if !ok {
				err := errors.New(fmt.Sprintf("异常退出，共完成写入股票%d支，无效数据%d支，全部%d支", count, nodata, total))
				s.WriteErrEvent(ctx, "异常退出", err)
				return err // task aborted
			}
		}

		if i == len(list)-1 {
			close(sem)
			s.WriteEvent(ctx, fmt.Sprintf("共完成写入股票%d支，无效数据%d支", count, nodata))
			return nil
		}

		time.Sleep(1 * time.Second)
	}
	return nil
}
